package com.yj.serial_port;

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;

import com.yj.serial_port.util.LogUtils;
import com.yj.serial_port.util.SerialPortHelper;


import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/** SerialPortPlugin */
public class SerialPortPlugin implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private MethodChannel channel;
  private EventChannel eventChannel;
  private EventChannel.EventSink eventSink;
  private SerialPortHelper serialPortHelper;
  private Handler handler;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "serial_port");
    channel.setMethodCallHandler(this);
    eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "serial_port/event");
    eventChannel.setStreamHandler(this);
    handler = new Handler(Looper.getMainLooper());
  }

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    switch (call.method) {
      case "getPlatformVersion":
        result.success("Android " + android.os.Build.VERSION.RELEASE);
        break;
      case "start":
        Integer baudrate = call.argument("baudrate");
        if(baudrate != null){
          initSerialPort(result, call.argument("device"), baudrate);
        }
        break;
      case "setLog":
        LogUtils.setIsDebug(Boolean.TRUE.equals(call.arguments()));
        break;
      case "close":
        try {
          closeSerialPort();
          LogUtils.v("串口关闭成功");
          result.success(true);
        } catch (Exception e) {
          e.printStackTrace();
          LogUtils.v("串口关闭失败");
          result.success(false);
        }
        break;
      default:
        result.notImplemented();
        break;
    }
  }

  String bytes2Hex(byte[] bytes){
    StringBuilder sb = new StringBuilder();
    if(bytes != null && bytes.length > 0){
      for(byte b : bytes){
        sb.append(byte2Hex(b));
      }
    }
    return sb.toString();
  }

  String byte2Hex(byte b){
    String hex = Integer.toHexString(b & 0xFF);
    if(hex.length() < 2){
      hex = 0 + hex;
    }
    return hex.toUpperCase();
  }

  void initSerialPort(Result result, String device, int baudrate){
    try {
      serialPortHelper = SerialPortHelper.getInstance(device, baudrate);
      serialPortHelper.setCallback((buffer, size) -> {
        if(eventSink != null){
          handler.post(()->{
            eventSink.success(bytes2Hex(buffer));
          });
        }
      });
      serialPortHelper.startReceive();
      LogUtils.v("串口启动成功");
      result.success(true);
    }catch (Exception e){
      e.printStackTrace();
      LogUtils.v("串口启动失败");
      result.success(false);
    }
  }

  void closeSerialPort(){
    if(serialPortHelper != null){
      serialPortHelper.close();
      serialPortHelper = null;
    }

  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
    channel = null;
    eventChannel.setStreamHandler(null);
    eventChannel = null;
    closeSerialPort();
    handler.removeCallbacksAndMessages(null);
//    handler.getLooper().quit();
    // normally we use new thread instead of main thread, but now we
    // use the main thread, Main thread not allowed to quit.
  }

  @Override
  public void onListen(Object arguments, EventChannel.EventSink events) {
    eventSink = events;
  }

  @Override
  public void onCancel(Object arguments) {
  }
}
